#include "stdafx.h"

#pragma managed
#include "FileEntity.h"
#include "..\Database.h"
using namespace System::Diagnostics;
using namespace System::Collections;
using namespace System::Collections::Generic;

namespace ManagedAPI
{
	// static method
	FileEntity^ FileEntity::Create(const UdbEntity& entity)
	{
		Debug::Assert(entity != nullptr);
		if (entity == nullptr)
		{
			return nullptr;
		}

		const char* keystr = udbEntityNameUnique(entity);
		String^ key = gcnew String(keystr);

		FileEntity^ result = nullptr;
		if (msFileList->ContainsKey(key))
		{
			result = msFileList[key];
			Debug::Assert(result != nullptr);
		}
		else
		{
			result = gcnew FileEntity(entity);
			msFileList->Add(key, result);
		}
		return result;
	}
	void FileEntity::Clear()
	{
		msFileList->Clear();
	}

	FileEntity::FileEntity(const UdbEntity& entity)
		: Entity(entity)
	{

	}

	array<ClassType^>^ 
	FileEntity::GetClassTypeDefinitions()
	{
		if (mClassTypeDefinitions == nullptr)
		{
			mClassTypeDefinitions = ConvertEntities<ClassType>("define", "class");
		}
		return mClassTypeDefinitions;
	}
	array<ClassType^>^ FileEntity::GetStructTypeDefinitions()
	{
		if (mStructTypeDefinitions == nullptr)
		{
			mStructTypeDefinitions = ConvertEntities<ClassType>("define", "struct");
		}
		return mStructTypeDefinitions;
	}
	array<Entity^>^ 
	FileEntity::GetEnumDefintions()
	{
		if (mEnumDefinitions == nullptr)
		{
			mEnumDefinitions = ConvertEntities<Entity>("define", "enum ~member");
		}
		return mEnumDefinitions;
	}
	array<MethodType^>^ 
	FileEntity::GetFunctionDefinitions()
	{
		if (mFunctionDefinitions == nullptr)
		{
			mFunctionDefinitions = ConvertEntities<MethodType>("define", "function ~Member");
		}
		return mFunctionDefinitions;
	}

	array<MethodType^>^ 
	FileEntity::GetFunctionDeclarations()
	{
		if (mFunctionDeclarations == nullptr)
		{
			mFunctionDeclarations = ConvertEntities<MethodType>("declare", "function ~Member");
		}
		return mFunctionDeclarations;
	}

	array<Entity^>^ 
	FileEntity::GetNamespacesDeclarations()
	{
		if (mNamespaceDefinitions == nullptr)
		{
			mNamespaceDefinitions = ConvertEntities<Entity>("declare", "namespace");
		}
		return mNamespaceDefinitions;
	}

	// Private Methods
	array<UdbEntity>^ FileEntity::GetFileRefs(char* kindA, char* kindB)
	{
#if LIST_REFERENCE_CRASHER_BUG
		UdbReference* refs = 0;
		int numFileRefs = udbEntityRefs( this->mptr, kindA, kindB, 1, &refs);
		Debug::Assert(refs != nullptr);
		
		array<UdbEntity>^ result = nullptr; 
		if ((refs) && (numFileRefs > 0))
		{		
			result = gcnew array<UdbEntity>(numFileRefs);
			for (int i = 0; i < numFileRefs; i++)
			{
				UdbReference fileRef = refs[i];
				Debug::Assert(fileRef != nullptr);
				UdbEntity file_entity = udbReferenceEntity( fileRef );
				Debug::Assert(file_entity != nullptr);
				result[i] = file_entity;
			}
			Debug::Assert(result->Length > 0);
			refs = 0;
		}
		return result;
		
#else
		UdbEntity thisEnt = GetUdbEntity();

		UdbReference* refs = 0;
		int numFileRefs = 0;
		udbListReferenceFile(thisEnt, &refs, &numFileRefs);
		Debug::Assert(refs != nullptr);

		int numRefs = 0;

		udbListReferenceFilter(refs, udbKindParse(kindA), udbKindParse(kindB), 0, &refs, &numRefs);
		
		array<UdbEntity>^ result = nullptr; 
		if ((numRefs > 0) && (refs != nullptr))
		{
			result = gcnew array<UdbEntity>( numRefs );
			Debug::Assert(result != nullptr);
			for (int j = 0; j < numRefs; j++)
			{
				UdbReference reff = refs[j];
				Debug::Assert(reff != nullptr);
				UdbEntity entity = udbReferenceEntity( reff );
				result[j] = entity;
			}
			Debug::Assert(result->Length > 0);
		}

		udbListReferenceFree(refs);
		refs = nullptr;

		return result;
#endif
	}

	template<typename T> array<T^>^ FileEntity::ConvertEntities(char* kindA, char* kindB)
	{
		array<UdbEntity>^ temp = this->GetFileRefs(kindA, kindB);
		List<T^>^ result = gcnew List<T^>();
		Debug::Assert(result != nullptr);
		if (temp != nullptr)
		{
			int count = temp->Length;
			for (int i = 0; i < count; i++)
			{
				UdbEntity ent = temp[i];
				result->Add( T::Create( ent ) );
			}
		}

		return result->ToArray();		
	}
	Lexer^ FileEntity::GetLexer()
	{
		// initialized on demand
		UdbEntity thisEnt = GetUdbEntity();
		return Lexer::Create(thisEnt);
	}
}
